﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace Xiplayer.Helpers
{
    public class MouseHook
    {
        #region pinvoke details

        private enum HookType : int
        {
            WH_JOURNALRECORD = 0,
            WH_JOURNALPLAYBACK = 1,
            WH_KEYBOARD = 2,
            WH_GETMESSAGE = 3,
            WH_CALLWNDPROC = 4,
            WH_CBT = 5,
            WH_SYSMSGFILTER = 6,
            WH_MOUSE = 7,
            WH_HARDWARE = 8,
            WH_DEBUG = 9,
            WH_SHELL = 10,
            WH_FOREGROUNDIDLE = 11,
            WH_CALLWNDPROCRET = 12,
            WH_KEYBOARD_LL = 13,
            WH_MOUSE_LL = 14
        }

        #region DependencyProperty




        #endregion
        //Declare the wrapper managed POINT class.
        //[StructLayout(LayoutKind.Sequential)]
        //public class POINT
        //{
        //    public int x;
        //    public int y;
        //}
        ////Declare the wrapper managed MouseHookStruct class.
        //[StructLayout(LayoutKind.Sequential)]
        //public class MouseHookStruct
        //{
        //    public POINT pt;
        //    public int hwnd;
        //    public int wHitTestCode;
        //    public int dwExtraInfo;
        //}

        //public struct KBDLLHOOKSTRUCT
        //{
        //    public UInt32 vkCode;
        //    public UInt32 scanCode;
        //    public UInt32 flags;
        //    public UInt32 time;
        //    public IntPtr extraInfo;
        //}

        [DllImport("user32.dll")]
        private static extern int SetWindowsHookEx(HookType code, HookProc func, IntPtr instance, int threadID);

        [DllImport("user32.dll")]
        private static extern int UnhookWindowsHookEx(int hook);



        //This is the Import for the CallNextHookEx function.
        //Use this function to pass the hook information to the next hook procedure in chain.
        [DllImport("user32.dll", CharSet = CharSet.Auto,
         CallingConvention = CallingConvention.StdCall)]
        private static extern int CallNextHookEx(int idHook, int nCode,
        IntPtr wParam, IntPtr lParam);

        #endregion

        //HookType _hookType = HookType.WH_KEYBOARD_LL;
        HookType _hookTypeMsg = HookType.WH_GETMESSAGE;
        HookType _hookTypeMouse = HookType.WH_MOUSE_LL;
        internal int _hookHandle = 0;
        HookProc _hookFunction = null;

        // hook method called by system
        private delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);

        // events

        internal delegate void HookEventHandler(object sender, MouseHookEventArgs e);
        internal event HookEventHandler HookInstallEvent;
        public event EventHandler mouseevent;

        public MouseHook()
        {
            _hookFunction = new HookProc(MouseHookProc);
            Install();
        }

        ~MouseHook()
        {
            Uninstall();
        }

        //// hook function called by system
        //private int HookCallback(int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
        //{
        //    if (code < 0)
        //        return CallNextHookEx(_hookHandle, code, wParam, ref lParam);

        //    // KeyUp event
        //    if ((lParam.flags & 0x80) != 0 && this.KeyUp != null)
        //        this.KeyUp(this, new HookEventArgs(lParam.vkCode));

        //    // KeyDown event
        //    if ((lParam.flags & 0x80) == 0 && this.KeyDown != null)
        //        this.KeyDown(this, new HookEventArgs(lParam.vkCode));

        //    return CallNextHookEx(_hookHandle, code, wParam, ref lParam);
        //}
        private int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            //Marshall the data from the callback.
            //MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));

            if (nCode < 0)
            {
                return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
            }
            else
            {
                //MouseHookEventArgs _MouseHookEventArgs = new MouseHookEventArgs(MyMouseHookStruct.pt);
                //this.MouseMove(this, _MouseHookEventArgs);
                ////Create a string variable that shows the current mouse coordinates.
                //String strCaption = "x = " +
                //        MyMouseHookStruct.pt.x.ToString("d") +
                //            "  y = " +
                //MyMouseHookStruct.pt.y.ToString("d");
                ////You must get the active form because it is a static function.
                //Form tempForm = Form.ActiveForm;

                ////Set the caption of the form.
                //tempForm.Text = strCaption;

                if (mouseevent != null)
                {
                    mouseevent(this, new MouseHookEventArgs(_hookHandle));
                }

                if (wParam.ToString() == "513")
                    hook.GloableClass.mouseDownEvent();

                if (wParam.ToString() == "516" || wParam.ToString() == "517")
                    return 1;
                else
                    return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
            }
        }
        internal void Install()
        {
            // make sure not already installed
            if (_hookHandle != 0)
                return;

            // need instance handle to module to create a system-wide hook
            Module[] list = System.Reflection.Assembly.GetExecutingAssembly().GetModules();
            System.Diagnostics.Debug.Assert(list != null && list.Length > 0);

            // install system-wide hook
            //_hookHandle = SetWindowsHookEx(_hookType,
            //    _hookFunction, Marshal.GetHINSTANCE(list[0]), 0);
            _hookHandle = SetWindowsHookEx(_hookTypeMouse, _hookFunction, Marshal.GetHINSTANCE(list[0]), 0);
            //if (_hookHandle == 0)
            //    MessageBox.Show(_hookTypeMouse + " failed");
            if (this.HookInstallEvent != null)
                this.HookInstallEvent(this, new MouseHookEventArgs(_hookHandle));
        }

        internal void Uninstall()
        {
            if (_hookHandle != 0)
            {
                // uninstall system-wide hook
                UnhookWindowsHookEx(_hookHandle);
                _hookHandle = 0;
            }
        }

        internal class MouseHookEventArgs : EventArgs
        {

            //public int X;
            //public int Y;
            public int HookHandle;
            public MouseHookEventArgs(int _hookHandle)
            {

                this.HookHandle = _hookHandle;
            }
        }
    }
}
